use std::cell::RefCell;
use std::fmt::{Debug, Display, format, Formatter, write};
use std::process::exit;
use std::rc::Rc;
use crate::callable::{LoxCallable, LoxClass, LoxInstance};
use crate::scanner::{LiteralValue, Token};
use crate::stmt::StmtClass;


pub trait ExprVisitor<R> {
    fn visit_binary_expr(&mut self, expr: &Binary) -> R;
    fn visit_grouping_expr(&mut self, expr: &Grouping) -> R;
    fn visit_literal(&self, expr: &Literal) -> R;
    fn visit_unary(&mut self, expr: &Unary) -> R;
    fn visit_triple(&mut self, expr: &Triple) -> R;
    fn visit_variable(&mut self, expr: &Variable) -> R;
    fn visit_assign(&mut self, expr: &Assign) -> R;
    fn visit_logical(&mut self, expr: &Logical) -> R;
    fn visit_call_expr(&mut self, expr: &Call) -> R;
    fn visit_get_expr(&mut self, expr: &Get) -> R;
    fn visit_set_expr(&mut self, expr: &Set) -> R;
    fn visit_this_expr(&mut self, expr: &This) -> R;
    fn visit_super_expr(&mut self, expr: &Super) -> R;
}

pub trait ExprTrait<R> {
    fn accept(&self, visitor: &mut dyn ExprVisitor<R>) -> R;
}

#[derive(Clone)]
pub enum EvaluatedValue {
    Literal(LiteralValue),
    Call(Rc<dyn LoxCallable>),
    Class(LoxClass),
    Instance(Rc<RefCell<LoxInstance>>)
}

impl Debug for EvaluatedValue {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            EvaluatedValue::Literal(l) => write!(f, "EvaluatedValue[{:?}]", l),
            EvaluatedValue::Call(c) => write!(f, "EvaluatedValue[Call]"),
            EvaluatedValue::Instance(instance) => write!(f, "EvaluatedValue[{}]", instance.borrow()),
            EvaluatedValue::Class(cls) => write!(f, "EvaluatedValue[Class {}]", cls.name)
        }
    }
}

impl EvaluatedValue {
    pub fn stringify(&self) -> String {
        match self {
            EvaluatedValue::Literal(l) => format!("{}", &l.stringify()),
            EvaluatedValue::Call(expr) => format!("Call[{:p}]", expr),
            EvaluatedValue::Instance(instance) => format!("{}", instance.borrow()),
            EvaluatedValue::Class(cls) => format!("Class {}", cls.name)
        }
    }
    pub fn new_string(s: String) -> Self {
        Self::Literal(LiteralValue::String(s))
    }

    pub fn new_number(n: f64) -> Self {
        Self::Literal(LiteralValue::Number(n))
    }

    pub fn new_bool(b: bool) -> Self {
        Self::Literal(LiteralValue::Bool(b))
    }

    pub fn empty() -> Self {
        Self::Literal(LiteralValue::Empty)
    }

    pub fn nil() -> Self {
        Self::Literal(LiteralValue::Nil)
    }
}

pub type EvaluateResult = Result<EvaluatedValue, String>;

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Binary {
    pub left: Rc<Expression>,
    pub operator: Token,
    pub right: Rc<Expression>
}

impl Display for Binary {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Binary[left={:?}, token={:?}, right={:?}]", self.left, self.operator, self.right)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Call {
    pub callee: Rc<Expression>,
    pub parent: Token,
    pub arguments: Vec<Expression>
}

impl Display for Call {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Binary[callee={:?}, parent={:?}, arguments={:?}]", self.callee, self.parent, self.arguments)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Grouping {
    pub expr: Rc<Expression>
}

impl Display for Grouping {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Grouping[{:?}]", self.expr)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Literal {
    pub value: LiteralValue
}

impl Display for Literal {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match &self.value {
            LiteralValue::String(s) => write!(f, "Literal[String={}]", s),
            LiteralValue::Number(ff) => write!(f, "Literal[Number={}]", *ff),
            LiteralValue::Bool(b) => write!(f, "Literal[Bool={}", *b),
            LiteralValue::Nil => write!(f, "Literal[Nil]"),
            LiteralValue::Empty => write!(f, "Literal[Empty]")
        }
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Unary {
    pub operator: Token,
    pub right: Rc<Expression>
}

impl Display for Unary {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Unary[operator={:?}, right={:?}", self.operator, self.right)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Variable {
    pub token: Token
}

impl Display for Variable {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Variable[token={:?}]", self.token)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Triple {
    pub judge_expr: Rc<Expression>,
    pub true_expr: Rc<Expression>,
    pub false_expr: Rc<Expression>
}

impl Display for Triple {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Triple[judge={:?}, true_expr={:?}, false_expr={:?}]", self.judge_expr, self.true_expr, self.false_expr)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Assign {
    pub name: Token,
    pub value: Rc<Expression>
}

impl Display for Assign {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Assign[token={:?}, value={:?}", self.name.lexeme, self.value)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Logical {
    pub left: Rc<Expression>,
    pub operator: Token,
    pub right: Rc<Expression>
}

impl Display for Logical {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Logical[left={:?}, operator={:?}, right={:?}", self.left, self.operator, self.right)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Get {
    pub object: Rc<Expression>,
    pub name: Token
}

impl Display for Get {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Get[object={:?}, name={}]", self.object, self.name.lexeme)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Set {
    pub object: Rc<Expression>,
    pub name: Token,
    pub value: Rc<Expression>
}

impl Display for Set {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "Set[object={:?}, name={:?}, value={:?}", self.object, self.name.lexeme, self.value)
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct This {
    pub keyword: Token
}

impl Display for This {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "This")
    }
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Super {
    pub keyword: Token,
    pub method: Token
}

#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Expression {
    Binary(Binary),
    Grouping(Grouping),
    Literal(Literal),
    Unary(Unary),
    Variable(Variable),
    Triple(Triple),
    Assign(Assign),
    Logical(Logical),
    Call(Call),
    Get(Get),
    Set(Set),
    This(This),
    Super(Super)
}

impl Display for Expression {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Expression::Binary(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Grouping(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Literal(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Unary(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Variable(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Triple(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Assign(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Logical(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Call(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Get(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::Set(v) => write!(f, "{}", format!("{}", v).as_str()),
            Expression::This(_) => write!(f, "this"),
            Expression::Super(_) => write!(f, "super"),
        }
    }
}

impl Expression {
    pub fn new_string(s: String) -> Self {
        Self::Literal(Literal { value: LiteralValue::String(s) })
    }

    pub fn new_number(n: f64) -> Self {
        Self::Literal(Literal { value: LiteralValue::Number(n)})
    }

    pub fn new_bool(b: bool) -> Self {
        Self::Literal(Literal { value: LiteralValue::Bool(b)})
    }

    pub fn empty() -> Self {
        Self::Literal(Literal { value: LiteralValue::Empty})
    }

    pub fn nil() -> Self {
        Self::Literal(Literal { value: LiteralValue::Nil})
    }
}

impl<R> ExprTrait<R> for Expression {
    fn accept(&self, visitor: &mut dyn ExprVisitor<R>) -> R {
        match self {
            Expression::Binary(expr) => visitor.visit_binary_expr(expr),
            Expression::Grouping(expr) => visitor.visit_grouping_expr(expr),
            Expression::Literal(expr) => visitor.visit_literal(expr),
            Expression::Unary(expr) => visitor.visit_unary(expr),
            Expression::Variable(expr) => visitor.visit_variable(expr),
            Expression::Triple(expr) => visitor.visit_triple(expr),
            Expression::Assign(expr) => visitor.visit_assign(expr),
            Expression::Logical(expr) => visitor.visit_logical(expr),
            Expression::Call(expr) => visitor.visit_call_expr(expr),
            Expression::Get(expr) => visitor.visit_get_expr(expr),
            Expression::Set(expr) => visitor.visit_set_expr(expr),
            Expression::This(expr) => visitor.visit_this_expr(expr),
            Expression::Super(expr) => visitor.visit_super_expr(expr)
        }
    }
}


pub struct PrintVisitor {}

impl ExprVisitor<String> for PrintVisitor {
    fn visit_binary_expr(&mut self, expr: &Binary) -> String {
        format!("{}", expr)
    }

    fn visit_grouping_expr(&mut self, expr: &Grouping) -> String {
        format!("{}", expr)
    }

    fn visit_literal(&self, expr: &Literal) -> String {
        format!("{}", expr)
    }

    fn visit_unary(&mut self, expr: &Unary) -> String {
        format!("{}", expr)
    }

    fn visit_triple(&mut self, expr: &Triple) -> String {
        format!("{}", expr)
    }

    fn visit_variable(&mut self, expr: &Variable) -> String {
        format!("{}", expr)
    }

    fn visit_assign(&mut self, expr: &Assign) -> String {
        format!("{}", expr)
    }

    fn visit_logical(&mut self, expr: &Logical) -> String {
        format!("{}", expr)
    }

    fn visit_call_expr(&mut self, expr: &Call) -> String {
        format!("{}", expr)
    }

    fn visit_get_expr(&mut self, expr: &Get) -> String {
        format!("{}", expr)
    }

    fn visit_set_expr(&mut self, expr: &Set) -> String {
        format!("{}", expr)
    }

    fn visit_this_expr(&mut self, expr: &This) -> String {
        "this".to_string()
    }

    fn visit_super_expr(&mut self, expr: &Super) -> String {
        "super".to_string()
    }
}

#[cfg(test)]
mod test {
    use crate::expr::{Expression, PrintVisitor, ExprVisitor, ExprTrait, Literal};
    use crate::scanner::LiteralValue;

    #[test]
    fn print_expr_test() {
        let mut visitor = PrintVisitor {};
        let literal = Expression::new_string("hello, literal!".to_string());
        let s = literal.accept(&mut visitor);
        println!("{}", s);
        //let visitor = Box::new(visitor);
        println!("{}", literal.accept(&mut visitor));
    }
}